package com.atguigu.java;

import org.testng.annotations.Test;

import java.io.*;

/**
 *
 * 一、流的分类：
 * 1.操作的数据单位：字节流、字符流
 * 2.数据的流向：输入流、输出流
 * 3.流的角色：节点流、处理流
 *
 * 二、流的体系结构
 * 抽象基类                  节点流(文件流)                                    处理流(缓冲流--->处理流的一种)
 * InputStream              FileInputStream(read(byte[] buffer))           BufferInputStream(read(byte[] buffer))
 * OutputStream             FileOutputStream(writer(byte[] buffer,0,len))  BufferOutputStream(writer(byte[] buffer,0,len)) / flush()
 * Reader                   FileReader(read(char[] cubf))                  BufferReader(read(char[] cubf))
 * Writer                   FileWriter(writer(char[] cubf,0,len))          BufferWriter(writer(char[] cubf,0,len)) / flush()
 *
 *
 */
public class FileReadWriterTest {

    //main方法中获取文件的绝对路径与test单元测试的不一样，main是相较于当前工程
    //Test是相较于当前module,如果想要使得二者保持相同，则需要添加module文件夹名
    public static void main(String[] args) {
        File file = new File("hello.txt");//相较于当前工程
        //F:\Java\IntelliJ IDEA\JavaSenior\JavaSenior\hello.txt
        System.out.println(file.getAbsolutePath());

        File file1 = new File("day09\\hello.txt");
        //F:\Java\IntelliJ IDEA\JavaSenior\JavaSenior\day09\hello.txt
        System.out.println(file1.getAbsolutePath());
    }

    /*
    将day09下的hello.txt文件内容读入到程序当中，并输出到控制台

    说明点：
    1.read()的理解：返回读入的一个字符，如果达到文件末尾就返回-1
    2.异常的处理，为了保证流资源一定可以关闭，需要使用try-catch-finally处理
    3.读入的文件一定要存在，否则就会报FileFoundException.

     */
    @Test
    public void testFileReader(){
        FileReader fr = null;
        try {
            //1.实例化File类型对象。指明要操作的文件
            File file = new File("hello.txt");//相较于当前module

            //F:\Java\IntelliJ IDEA\JavaSenior\JavaSenior\day09\hello.txt
            System.out.println(file.getAbsolutePath());

            //2.提供具体的流
            fr = new FileReader(file);

            //3.数据的读入
            //read():返回读入的一个字符，如果达到文件末尾，则返回-1
            //方式一：
//        int data = fr.read();
//        while(data != -1){
//            System.out.print((char)data);
//            data = fr.read();
//        }

            //方式二：语法上对于方式一的修改
            int data;
            while((data = fr.read()) != -1){
                System.out.print((char)data);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //4.流的关闭
            try {
                if(fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    //对read()操作升级，使用read()的重载方法
    @Test
    public void testFileReader1(){
        FileReader fr = null;
        try {
            //1.File类的实例化
            File file = new File("hello.txt");

            //2.FileReader流的实例化
            fr = new FileReader(file);

            //3.读入的操作
            //read(char[] cubf):返回每次读入cubf数组中字符的个数，如果达到文件末尾，就返回-1
            char[] cbuf = new char[5];
            int len;
            while ((len = fr.read(cbuf)) != -1){
                  //方式一
                  //错误的方法
                      /*
                      错误分析：第一次读入5个-->“hello”,第二次读入5个-->"world",
                      第三次读入5个,由于不够所以123会代替“wor”,使得结果变成“123ld”.
                      */
//                for(int i = 0;i < cbuf.length;i++){
//                    //helloworld123ld
//                    System.out.print(cbuf[i]);
//                }

                //正确的写法
//                for(int i = 0;i < len;i++){
//                    System.out.print(cbuf[i]);
//                }

                //方式二
                //错误的写法，对应方式一的错误写法
//                String str = new String(cbuf);
//                System.out.print(cbuf);

                //正确的写法
                String str = new String(cbuf,0,len);
                System.out.print(str);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //4.资源的关闭
            try {
                if(fr != null) {
                    fr.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    /*
    从内存中写出数据到硬盘的文件里。

    说明：
    1.输出操作，对应的File可以不存在，
    2.
        File对应的硬盘中的文件如果不存在，在输出的过程中，会自动创建文件，
        File对应的文件如果存在：
                如果流使用的构造器是：FileWriter(file,false)（在默认状态下就是false） / FileWriter(file):对原有文件的覆盖
                如果流使用的构造器是：FileWriter(file,true):不会对原有的文件进行覆盖而是会在原有文件的内容上追加内容
     */
    @Test
    public void testFileWriter() throws IOException {
        //1.提供File类的对象，指明写出的文件
        File file = new File("hello1.txt");
        //2.提供FileWriter的对象，用于数据的写出
        FileWriter fr = new FileWriter(file,false);
        //3.写出的操作
        fr.write("I have a dream!\n");
        fr.write("you need to have a dream!\n");
        //4.流资源的关闭
        fr.close();
    }
    @Test
    public void testFileReadFileWriter(){
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.创建File类的对象，指明读入和写出的文件
            File srcFile = new File("hello.txt");
            File destFile = new File("hello2.txt");

            //不能使用字符流来处理图片等字节数据
//            File srcFile = new File("下午 趴在桌子的女孩4k动漫壁纸3840x2160_彼岸图网.jpg");
//            File destFile = new File("下午 趴在桌子的女孩4k动漫壁纸3840x2160_彼岸图网1.jpg");

            //2.创建输入流和输出流的对象
            fr = new FileReader(srcFile);
            fw = new FileWriter(destFile);

            //3.数据的读入和写出操作
            char[] cbuf = new char[5];
            int len;//记录每次读入到cubf数组中的字符的个数
            while ((len = fr.read(cbuf)) != -1) {
                    //每次写出len个字符
                    fw.write(cbuf,0,len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //4.关闭流资源
            //总结：方式一和方式二写法都正确，只是方式二考虑到fw如果出现异常，fr无法正常关闭
            //实际上try-catch方法就已经将该异常处理了，fr肯定能正常关闭
            //方式一：
//            try {
//                if(fw != null)
//                fw.close();
//            } catch (IOException e) {
//                throw new RuntimeException(e);
//            }
//            try {
//                if(fr != null)
//                fr.close();
//            } catch (IOException e) {
//                throw new RuntimeException(e);
//            }

            //方式二：
            try {
                if(fw != null)
                    fw.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }finally{
                try {
                    if(fr != null)
                        fr.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}
